using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms.Design;

namespace MKDS_Course_Editor
{
    public partial class BNCL : Form
    {
        SpecialPictureBox selected;
        string file;
        string ncer;
        string ncgr;
        string nclr;
        bool isfirst = true;
        int objselected = -1;
        public BNCL(string file, string ncer, string ncgr, string nclr)
        {
            InitializeComponent();
            this.DoubleBuffered = true;
            this.file = file;
            this.ncer = ncer;
            this.ncgr = ncgr;
            this.nclr = nclr;
            cl = Tinke.Imagen_NCLR.Leer(nclr);
            ce = Tinke.Imagen_NCER.Leer(ncer);
            cg = Tinke.Imagen_NCGR.Leer(ncgr);
            propertyGridEx1.Item.Add("Show Overlays", true, false, "View", "", true);
            propertyGridEx1.Item.Add("X Position", (byte)0, true, "Object", "", true);
            propertyGridEx1.Item.Add("Y Position", (byte)0, true, "Object", "", true);
            propertyGridEx1.Item.Add("Cell Number", 0, true, "Object", "", true);
        }
        BNCL_File b = new BNCL_File();
        BNBL_File bb = new BNBL_File();
        PluginInterface.NCER ce;
        PluginInterface.NCLR cl;
        PluginInterface.NCGR cg;
        bool loaded = false;
        public struct BNCL_File
        {
            public Header header;
            public struct Header
            {
                public string Stamp;
                public int nrObject;
            }
            public Objects objects;
            public struct Objects
            {
                public int[] X;
                public byte[] Unknown1;
                public int[] Y;
                public byte[] Unknown2;
                public int[] Cell;
            }
        }
        public struct BNBL_File
        {
            public Header header;
            public struct Header
            {
                public string Stamp;
                public int nrObject;
            }
            public Objects objects;
            public struct Objects
            {
                public int[] X;
                public byte[] Unknown1;
                public int[] Y;
                public byte[] Unknown2;
                public int[] Width;
                public int[] Height;
            }
        }
        Bitmap TrimBitmap(Bitmap source)
        {
            Rectangle srcRect = default(Rectangle);
            BitmapData data = null;
            try
            {
                data = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
                byte[] buffer = new byte[data.Height * data.Stride];
                Marshal.Copy(data.Scan0, buffer, 0, buffer.Length);
                int xMin = int.MaxValue;
                int xMax = 0;
                int yMin = int.MaxValue;
                int yMax = 0;
                for (int y = 0; y < data.Height; y++)
                {
                    for (int x = 0; x < data.Width; x++)
                    {
                        byte alpha = buffer[y * data.Stride + 4 * x + 3];
                        if (alpha != 0)
                        {
                            if (x < xMin) xMin = x;
                            if (x > xMax) xMax = x;
                            if (y < yMin) yMin = y;
                            if (y > yMax) yMax = y;
                        }
                    }
                }
                if (xMax < xMin || yMax < yMin)
                {
                    // Image is empty... 
                    return null;
                }
                srcRect = Rectangle.FromLTRB(xMin, yMin, xMax+1, yMax+1);
            }
            finally
            {
                if (data != null)
                    source.UnlockBits(data);
            }

            Bitmap dest = new Bitmap(srcRect.Width, srcRect.Height);
            Rectangle destRect = new Rectangle(0, 0, srcRect.Width, srcRect.Height);
            using (Graphics graphics = Graphics.FromImage(dest))
            {
                graphics.DrawImage(source, destRect, srcRect, GraphicsUnit.Pixel);
            }
            return dest;
        }



        private void BNCL_Load(object sender, EventArgs e)
        {
            EndianBinaryReader er = new EndianBinaryReader(File.OpenRead(file), Endianness.LittleEndian);
            b.header.Stamp = er.ReadString(Encoding.ASCII, 4);
            if (b.header.Stamp == "JNCL")
            {
                er.ReadBytes(2);
                b.header.nrObject = er.ReadInt16();
                b.objects.X = new int[b.header.nrObject];
                b.objects.Unknown1 = new byte[b.header.nrObject];
                b.objects.Y = new int[b.header.nrObject];
                b.objects.Unknown2 = new byte[b.header.nrObject];
                b.objects.Cell = new int[b.header.nrObject];
                for (int i = 0; i < b.header.nrObject; i++)
                {
                    b.objects.X[i] = er.ReadByte();//er.ReadInt16();
                    b.objects.Unknown1[i] = er.ReadByte();
                    b.objects.Y[i] = er.ReadByte();//er.ReadInt16();
                    b.objects.Unknown2[i] = er.ReadByte();
                    b.objects.Cell[i] = er.ReadInt32();
                }
                er.Close();

                if (File.Exists(file.Replace(".bncl", ".bnbl")))
                {
                    EndianBinaryReader er2 = new EndianBinaryReader(File.OpenRead(file.Replace(".bncl", ".bnbl")), Endianness.LittleEndian);
                    bb.header.Stamp = er2.ReadString(Encoding.ASCII, 4);
                    if (bb.header.Stamp == "JNBL")
                    {
                        er2.ReadBytes(2);
                        bb.header.nrObject = er2.ReadInt16();
                        bb.objects.X = new int[bb.header.nrObject];
                        bb.objects.Unknown1 = new byte[bb.header.nrObject];
                        bb.objects.Y = new int[bb.header.nrObject];
                        bb.objects.Unknown2 = new byte[bb.header.nrObject];
                        bb.objects.Width = new int[bb.header.nrObject];
                        bb.objects.Height = new int[bb.header.nrObject];
                        for (int i = 0; i < bb.header.nrObject; i++)
                        {
                            bb.objects.X[i] = er2.ReadByte();//er2.ReadInt16();
                            bb.objects.Unknown1[i] = er2.ReadByte();
                            bb.objects.Y[i] = er2.ReadByte();//er2.ReadInt16();
                            bb.objects.Unknown2[i] = er2.ReadByte();
                            bb.objects.Width[i] = (int)er2.ReadByte();
                            bb.objects.Height[i] = (int)er2.ReadByte();
                        }
                        er2.Close();


                    }
                    else
                    {
                        er2.Close();
                    }
                }
            }
            else
            {
                er.Close();
                MessageBox.Show("This isn't a BNCL file.");
            }
            isfirst = false;
        }

        void p_Selection(object sender, EventArgs e)
        {
            if (selected != null)
            {
                selected.Selected = false;
            }
            selected = (SpecialPictureBox)sender;
        }

        void p_Click(object sender, EventArgs e)
        {
            SpecialPictureBox p = (SpecialPictureBox)sender;
            p.Selected = true;
        }

        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
            if (selected != null)
            {
                switch (keyData)
                {
                    case Keys.Left:
                        if (selected.Right != 1)
                        {
                            selected.Left -= 1;
                        }
                        return true;
                    case Keys.Right:
                        if (selected.Left != 255)
                        {
                            selected.Left += 1;
                        }
                        return true;
                    case Keys.Up:
                        if (selected.Bottom != 1)
                        {
                            selected.Top -= 1;
                        }
                        return true;
                    case Keys.Down:
                        if (selected.Top != 191)
                        {
                            selected.Top += 1;
                        }
                        return true;
                    default:
                        return base.ProcessCmdKey(ref msg, keyData);
                }
            }
            else
            {
                return base.ProcessCmdKey(ref msg, keyData);
            }
        }

        void p2_Click(object sender, EventArgs e)
        {
            SpecialPanel p = (SpecialPanel)sender;
            p.Selected = true;
        }

        private void BNCL_Paint(object sender, PaintEventArgs e)
        {
            if (isfirst == false)
            {


                for (int i = 0; i < b.header.nrObject; i++)
                {
                    //SpecialPictureBox p = new SpecialPictureBox();
                    //p.SizeMode = PictureBoxSizeMode.AutoSize;
                    Bitmap bit = new Bitmap(TrimBitmap(Tinke.Imagen_NCER.Obtener_Imagen(ce.cebk.banks[b.objects.Cell[i]], ce.cebk.block_size, cg, cl, false, false, false, /*true*/false, true)));
                    bit.MakeTransparent(cl.pltt.paletas[cg.rahc.tileData.nPaleta[0]].colores[0]);
                    //p.Image = bit;
                    Point po = new Point();
                    switch (b.objects.Unknown1[i])
                    {
                        case 0x00:
                            po.X = b.objects.X[i];
                            break;
                        case 0x01:
                            po.X = b.objects.X[i] * bit.Width;
                            break;
                        case 0x0F:
                            po.X = (0xFF - b.objects.X[i]) * 2;
                            break;
                        case 0x10:
                            po.X = b.objects.X[i] - (bit.Width / 2);
                            break;
                        case 0x20:
                            po.X = b.objects.X[i] - (bit.Width);
                            break;
                        case 0x21:
                            po.X = 256 - b.objects.X[i] - bit.Width;
                            break;
                        default:
                            po.X = b.objects.X[i];
                            break;
                    }
                    switch (b.objects.Unknown2[i])
                    {
                        case 0x00:
                            po.Y = b.objects.Y[i];
                            break;
                        case 0x01:
                            po.Y = b.objects.Y[i] * bit.Height;
                            break;
                        case 0x0F:
                            po.Y = (0xFF - b.objects.Y[i]) * 2;
                            break;
                        case 0x10:
                            po.Y = b.objects.Y[i] - (bit.Height / 2);
                            break;
                        case 0x20:
                            po.Y = b.objects.Y[i] - (bit.Height);
                            break;
                        case 0x21:
                            po.Y = 256 - b.objects.Y[i] - bit.Height;
                            break;
                        default:
                            po.Y = b.objects.Y[i];
                            break;
                    }
                    //p.Location = po;
                    //p.BackColor = Color.Transparent;
                    //p.Click += new EventHandler(p_Click);
                    //p.Selection += new EventHandler(p_Selection);
                    //this.Controls.Add(p);
                    //p.BringToFront();
                    e.Graphics.DrawImage(bit, po);
                    // ControlDesigner t = new System.Windows.Forms.Design.ControlDesigner();
                    // System.Windows.Forms.Design.ParentControlDesigner p2 = new System.Windows.Forms.Design.ParentControlDesigner();
                    //p2.Initialize(this);
                    // t.Initialize(p);
                    if (i == objselected)
                    {
                        e.Graphics.DrawLines(new Pen(new System.Drawing.Drawing2D.HatchBrush(System.Drawing.Drawing2D.HatchStyle.SmallCheckerBoard, Color.Black, Color.White)), new Point[] { po, new Point(po.X + bit.Width - 1, po.Y), new Point(po.X + bit.Width - 1, po.Y+bit.Height - 1), new Point(po.X, po.Y+bit.Height - 1), po });
                    }
                }
                if (File.Exists(file.Replace(".bncl", ".bnbl")) && (bool)propertyGridEx1.Item[0].Value == true)
                {
                    for (int i = 0; i < bb.header.nrObject; i++)
                    {
                        //SpecialPanel p = new SpecialPanel();
                        //p.BackColor = Color.FromArgb(30, Color.Yellow.R, Color.Yellow.G, Color.Yellow.B);
                        Point po = new Point();
                        switch (bb.objects.Unknown1[i])
                        {
                            case 0x00:
                                po.X = bb.objects.X[i];
                                break;
                            case 0x01:
                                po.X = bb.objects.X[i] * bb.objects.Width[i];
                                break;
                            case 0x0F:
                                po.X = (0xFF - bb.objects.X[i]) * 2;
                                break;
                            case 0x10:
                                po.X = bb.objects.X[i] - (bb.objects.Width[i] / 2);
                                break;
                            case 0x20:
                                po.X = bb.objects.X[i] - (bb.objects.Width[i]);
                                break;
                            case 0x21:
                                po.X = 256 - bb.objects.X[i] - bb.objects.Width[i];
                                break;
                            default:
                                po.X = bb.objects.X[i];
                                break;
                        }
                        switch (bb.objects.Unknown2[i])
                        {
                            case 0x00:
                                po.Y = bb.objects.Y[i];
                                break;
                            case 0x01:
                                po.Y = bb.objects.Y[i] * bb.objects.Height[i];
                                break;
                            case 0x0F:
                                po.Y = (0xFF - bb.objects.Y[i]) * 2;
                                break;
                            case 0x10:
                                po.Y = bb.objects.Y[i] - (bb.objects.Height[i] / 2);
                                break;
                            case 0x20:
                                po.Y = bb.objects.Y[i] - (bb.objects.Height[i]);
                                break;
                            case 0x21:
                                po.Y = 256 - bb.objects.Y[i] - bb.objects.Height[i];
                                break;
                            default:
                                po.Y = bb.objects.Y[i];
                                break;
                        }
                        //p.Location = po;
                        //p.Width = bb.objects.Width[i];
                        //p.Height = bb.objects.Height[i];
                        //p.Click += new EventHandler(p2_Click);
                        //this.Controls.Add(p);
                        //p.BringToFront();
                        e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(127, Color.Yellow.R, Color.Yellow.G, Color.Yellow.B)), po.X, po.Y, bb.objects.Width[i], bb.objects.Height[i]);
                        //LayoutEngine.InitLayout(p, BoundsSpecified.All);
                    }
                }
            }
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            if (File.Exists(file.Replace(".bncl", ".bnbl")))
            {
                this.Invalidate();
            }
        }

        private void BNCL_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Left)
            {
                int nr = -1;
                Point po = new Point();
                for (int i = 0; i < b.header.nrObject; i++)
                {
                    Bitmap bit = new Bitmap(TrimBitmap(Tinke.Imagen_NCER.Obtener_Imagen(ce.cebk.banks[b.objects.Cell[i]], ce.cebk.block_size, cg, cl, false, false, false, /*true*/false, true)));
                    bit.MakeTransparent(cl.pltt.paletas[cg.rahc.tileData.nPaleta[0]].colores[0]);
                    switch (b.objects.Unknown1[i])
                    {
                        case 0x00:
                            po.X = b.objects.X[i];
                            break;
                        case 0x01:
                            po.X = b.objects.X[i];
                            break;
                        case 0x0F:
                            po.X = (0xFF - b.objects.X[i]) * 2;
                            break;
                        case 0x10:
                            po.X = b.objects.X[i] - (bit.Width / 2);
                            break;
                        case 0x20:
                            po.X = b.objects.X[i] - (bit.Width);
                            break;
                        case 0x21:
                            po.X = 256 - b.objects.X[i] - bit.Width;
                            break;
                        default:
                            po.X = b.objects.X[i];
                            break;
                    }
                    switch (b.objects.Unknown2[i])
                    {
                        case 0x00:
                            po.Y = b.objects.Y[i];
                            break;
                        case 0x01:
                            po.Y = b.objects.Y[i] * bit.Height;
                            break;
                        case 0x0F:
                            po.Y = (0xFF - b.objects.Y[i]) * 2;
                            break;
                        case 0x10:
                            po.Y = b.objects.Y[i] - (bit.Height / 2);
                            break;
                        case 0x20:
                            po.Y = b.objects.Y[i] - (bit.Height);
                            break;
                        case 0x21:
                            po.Y = 256 - b.objects.Y[i] - bit.Height;
                            break;
                        default:
                            po.Y = b.objects.Y[i];
                            break;
                    }
                    if (po.X <= e.X && po.X + bit.Width >= e.X && po.Y <= e.Y && po.Y + bit.Height >= e.Y && bit.GetPixel(e.X-po.X, e.Y-po.Y) != Color.Transparent)
                    {
                        nr = i;
                    }
                }
                if (objselected != nr)
                {
                    objselected = nr;
                    this.Invalidate();
                    Bitmap bit = new Bitmap(TrimBitmap(Tinke.Imagen_NCER.Obtener_Imagen(ce.cebk.banks[b.objects.Cell[nr]], ce.cebk.block_size, cg, cl, false, false, false, /*true*/false, true)));
                    bit.MakeTransparent(cl.pltt.paletas[cg.rahc.tileData.nPaleta[0]].colores[0]);
                    switch (b.objects.Unknown1[nr])
                    {
                        case 0x00:
                            po.X = b.objects.X[nr];
                            break;
                        case 0x01:
                            po.X = b.objects.X[nr] * bit.Width;
                            break;
                        case 0x0F:
                            po.X = (0xFF - b.objects.X[nr]) * 2;
                            break;
                        case 0x10:
                            po.X = b.objects.X[nr] - (bit.Width / 2);
                            break;
                        case 0x20:
                            po.X = b.objects.X[nr] - (bit.Width);
                            break;
                        case 0x21:
                            po.X = 256 - b.objects.X[nr] - bit.Width;
                            break;
                        default:
                            po.X = b.objects.X[nr];
                            break;
                    }
                    switch (b.objects.Unknown2[nr])
                    {
                        case 0x00:
                            po.Y = b.objects.Y[nr];
                            break;
                        case 0x01:
                            po.Y = b.objects.Y[nr] * bit.Height;
                            break;
                        case 0x0F:
                            po.Y = (0xFF - b.objects.Y[nr]) * 2;
                            break;
                        case 0x10:
                            po.Y = b.objects.Y[nr] - (bit.Height / 2);
                            break;
                        case 0x20:
                            po.Y = b.objects.Y[nr] - (bit.Height);
                            break;
                        case 0x21:
                            po.Y = 256 - b.objects.Y[nr] - bit.Height;
                            break;
                        default:
                            po.Y = b.objects.Y[nr];
                            break;
                    }
                    if (objselected != -1)
                    { 
                        propertyGridEx1.Item[1].Value = po.X;
                        propertyGridEx1.Item[1].IsReadOnly = false;
                        propertyGridEx1.Item[2].Value = po.Y;
                        propertyGridEx1.Item[2].IsReadOnly = false;
                        propertyGridEx1.Item[3].Value = b.objects.Cell[nr];
                        propertyGridEx1.Item[3].IsReadOnly = false;
                        propertyGridEx1.Refresh();
                    }
                    else
                    {
                        propertyGridEx1.Item[1].Value = 0;
                        propertyGridEx1.Item[1].IsReadOnly = true;
                        propertyGridEx1.Item[2].Value = 0;
                        propertyGridEx1.Item[2].IsReadOnly = true;
                        propertyGridEx1.Item[3].Value = 0;
                        propertyGridEx1.Item[3].IsReadOnly = true;
                        propertyGridEx1.Refresh();
                    }
                }
            }
        }

        private void propertyGridEx1_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
        {
            if (e.OldValue != e.ChangedItem.Value)
            {
                switch (e.ChangedItem.Label)
                {
                    case "Show Overlays":
                        this.Invalidate();
                        break;
                    case "X Position":
                        b.objects.X[objselected] = (int)e.ChangedItem.Value;
                        b.objects.Unknown1[objselected] = 0;
                        this.Invalidate();
                        break;
                    case "Y Position":
                        b.objects.Y[objselected] = (int)e.ChangedItem.Value;
                        b.objects.Unknown2[objselected] = 0;
                        this.Invalidate();
                        break;
                    case "Cell Number":
                        b.objects.Cell[objselected] = (int)e.ChangedItem.Value;
                        this.Invalidate();
                        break;
                }
            }
        }

        private void propertyGridEx1_SelectedObjectsChanged(object sender, EventArgs e)
        {

        }

        private void button1_Click(object sender, EventArgs e)
        {
            EndianBinaryWriter wr = new EndianBinaryWriter(File.OpenWrite(file), Endianness.LittleEndian);
            wr.Write("JNCL", Encoding.ASCII, false);
            wr.Write((Int16)0);
            wr.Write((Int16)b.header.nrObject);
            for (int i = 0; i < b.header.nrObject; i++)
            {
                wr.Write((byte)b.objects.X[i]);
                wr.Write((byte)b.objects.Unknown1[i]);
                wr.Write((byte)b.objects.Y[i]);
                wr.Write((byte)b.objects.Unknown2[i]);
                wr.Write((Int32)b.objects.Cell[i]);
            }
            wr.Close();
        }

        private void toolStripButton1_Click(object sender, EventArgs e)
        {
            EndianBinaryWriter wr = new EndianBinaryWriter(File.OpenWrite(file), Endianness.LittleEndian);
            wr.Write("JNCL", Encoding.ASCII, false);
            wr.Write((Int16)0);
            wr.Write((Int16)b.header.nrObject);
            for (int i = 0; i < b.header.nrObject; i++)
            {
                wr.Write((byte)b.objects.X[i]);
                wr.Write((byte)b.objects.Unknown1[i]);
                wr.Write((byte)b.objects.Y[i]);
                wr.Write((byte)b.objects.Unknown2[i]);
                wr.Write((Int32)b.objects.Cell[i]);
            }
            wr.Close();
        }

        private void toolStripButton1_Click_1(object sender, EventArgs e)
        {
            EndianBinaryWriter wr = new EndianBinaryWriter(File.Create(file), Endianness.LittleEndian);
            wr.Write("JNCL", Encoding.ASCII, false);
            wr.Write((Int16)0);
            wr.Write((Int16)b.header.nrObject);
            for (int i = 0; i < b.header.nrObject; i++)
            {
                wr.Write((byte)b.objects.X[i]);
                wr.Write((byte)b.objects.Unknown1[i]);
                wr.Write((byte)b.objects.Y[i]);
                wr.Write((byte)b.objects.Unknown2[i]);
                wr.Write((Int32)b.objects.Cell[i]);
            }
            wr.Close();
        }

        private void toolStripButton2_Click(object sender, EventArgs e)
        {
            b.header.nrObject += 1;
            List<int> c = new List<int>();
            c.AddRange(b.objects.Cell);
            c.Add(0);
            b.objects.Cell = c.ToArray();
            List<int> x = new List<int>();
            x.AddRange(b.objects.X);
            x.Add(0);
            b.objects.X = x.ToArray();
            List<byte> u1 = new List<byte>();
            u1.AddRange(b.objects.Unknown1);
            u1.Add(0);
            b.objects.Unknown1 = u1.ToArray();
            List<int> y = new List<int>();
            y.AddRange(b.objects.Y);
            y.Add(0);
            b.objects.Y = y.ToArray();
            List<byte> u2 = new List<byte>();
            u2.AddRange(b.objects.Unknown2);
            u2.Add(0);
            b.objects.Unknown2 = u2.ToArray();
            this.Invalidate();
        }

        private void toolStripButton3_Click(object sender, EventArgs e)
        {
            if (objselected != -1)
            {
                b.header.nrObject -= 1;
                List<int> c = new List<int>();
                c.AddRange(b.objects.Cell);
                c.RemoveAt(objselected);
                b.objects.Cell = c.ToArray();
                List<int> x = new List<int>();
                x.AddRange(b.objects.X);
                x.RemoveAt(objselected);
                b.objects.X = x.ToArray();
                List<byte> u1 = new List<byte>();
                u1.AddRange(b.objects.Unknown1);
                u1.RemoveAt(objselected);
                b.objects.Unknown1 = u1.ToArray();
                List<int> y = new List<int>();
                y.AddRange(b.objects.Y);
                y.RemoveAt(objselected);
                b.objects.Y = y.ToArray();
                List<byte> u2 = new List<byte>();
                u2.AddRange(b.objects.Unknown2);
                u2.RemoveAt(objselected);
                b.objects.Unknown2 = u2.ToArray();
                this.Invalidate();
            }
        }
    }
    

}
